﻿#region COPYRIGHT
//--------------------------------------------------------------------------------
// <copyright file="TextBox.cs" company="starLiGHT Entertainment Studios">
//    Copyright (c) 2011 
//       Roland Rosenkranz (Glatzemann@email.de)
// </copyright>
// <license>
//   This file is part of starLiGHT.GUI.
//
//   It is released under the Microsoft Public License (Ms-PL).
//
//   You should have received a copy of the Microsoft Public License
//   along with starLiGHT.GUI. If not, see http://sle.codeplex.com/license.
//    
//   ADDITIONAL (commercial) LICENSES for starLiGHT.GUI are available on request.
// </license>
// <version>
// **************[ starLiGHT.Engine SVN ]**********************
// * $Rev:: 9631                  $:  Revision of last commit *
// * $Author:: unknown            $:  Author of last commit   *
// * $Date:: 2011-05-03 11:00:42 #$:  Date of last commit     *
// ************************************************************
// </version>
//--------------------------------------------------------------------------------
#endregion

namespace starLiGHT.GUI.Widgets
{
    #region Using Statements
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.GamerServices;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using starLiGHT.GUI.Input;
    #endregion

#if !WINDOWS
    public struct GuideKeyboardInfo
    {
        private string title;
        private string description;
        private PlayerIndex playerIndex;

        public string Title { get { return title; } set { title = value; } }
        public string Description { get { return description; } set { description = value; } }
        public PlayerIndex PlayerIndex { get { return playerIndex; } set { playerIndex = value; } }

        public GuideKeyboardInfo(string title, string description)
        {
            this.title = title;
            this.description = description;
            this.playerIndex = PlayerIndex.One;
        }
        public GuideKeyboardInfo(string title, string description, PlayerIndex playerIndex)
        {
            this.title = title;
            this.description = description;
            this.playerIndex = playerIndex;
        }
    }
#endif

    public class TextBox : Widget, IUpdateableWidget
    {
        #region Constants
        private const float BlinkDuration = 0.667f;
        private const float CaretMarginLeft = 5;
        private const float CaretMarginRight = 5;
        #endregion

        #region Private Members
        private TextInputTracker inputTracker =
#if WINDOWS
 new KeyboardTextInputTracker();
#else
 null;
#endif
        private float caretBlinkTimer;
        private Vector2 caretLocation;
        private float overlap = 0;
        #endregion

        #region Constructors
        public TextBox(GuiManager manager, string name)
            : base(manager, name)
        {
            if (inputTracker != null)
            {
                this.inputTracker.TextChanged += this.TextChanged;
                this.inputTracker.CharacterValidCheck += this.CharacterValidCheck;
                this.inputTracker.CaretPositionChanged += this.UpdateCaretLocation;
            }
        }

#if !WINDOWS
        public TextBox(GuiManager manager, string name, GuideKeyboardInfo keyboardInfo)
            : this(manager, name)
        {
            this.KeyboardInfo = keyboardInfo;
        }
#endif
        #endregion

        public bool UsePasswordMode
        {
            get;
            set;
        }

#if !WINDOWS
        GuideKeyboardInfo KeyboardInfo 
        { 
            get; 
            set; 
        }
#endif

        public TextInputTracker TextInputTracker
        {
            get
            {
                return this.inputTracker;
            }

            set
            {
                if (this.inputTracker != null)
                {
                    this.inputTracker.TextChanged -= this.TextChanged;
                    this.inputTracker.CharacterValidCheck -= this.CharacterValidCheck;
                    this.inputTracker.CaretPositionChanged -= this.UpdateCaretLocation;
                }

                this.inputTracker = value;

                if (this.inputTracker != null)
                {
                    this.inputTracker.TextChanged += this.TextChanged;
                    this.inputTracker.CharacterValidCheck += this.CharacterValidCheck;
                    this.inputTracker.CaretPositionChanged += this.UpdateCaretLocation;
                }
            }
        }

        public override bool CanFocus
        {
            get
            {
                return true;
            }
        }

        private bool IsCaretVisible
        {
            get
            {
                return this.caretBlinkTimer < BlinkDuration;
            }
        }

        public override void OnGotFocus(object sender, EventArgs e)
        {
            base.OnGotFocus(sender, e);
            this.StartTracking(Manager.PointerPosition);
        }

        public override void OnLostFocus(object sender, EventArgs e)
        {
            base.OnLostFocus(sender, e);
            this.StopTracking();
        }

        public override void Draw(GameTime gameTime, IGuiRenderer renderer)
        {
            if (Visible && CurrentState != null)
            {
                foreach (Layer layer in CurrentState.Layers)
                {
                    if (layer.Name == "Caret")
                    {
                        if (this.IsCaretVisible)
                        {
                            float w = layer.Image.Rectangle.Width;
                            float h = layer.Image.Rectangle.Height;

                            renderer.DrawSprite(
                                                layer.Image.Texture, 
                                                0,
                                                (int)(ScreenLocation.X + this.caretLocation.X - this.overlap), 
                                                (int)(ScreenLocation.Y + this.caretLocation.Y), 
                                                w, 
                                                h,
                                                layer.Image.Rectangle.X, 
                                                layer.Image.Rectangle.Y, 
                                                w, 
                                                h,
                                                Tint);
                        }
                    }
                    else
                    {
                        renderer.DrawLayer(this, layer);
                    }

                    foreach (TextLabel te in layer.TextLabels)
                    {
                        this.DrawText(renderer, layer, te);
                    }
                }
            }
        }

        public override void DrawText(IGuiRenderer renderer, Layer layer, TextLabel te)
        {
            if (!string.IsNullOrEmpty(Text) && te.Name.Equals("main", StringComparison.InvariantCultureIgnoreCase))
            {
                renderer.ScissorPush(ScissorRectangle);
                Vector2 pos = new Vector2(ClientRectangle.Left - this.overlap, ClientRectangle.Top);
                renderer.DrawString(Font, Text, pos, ForeColor);
                renderer.ScissorPop();
            }
        }
        
        public override void OnMouseDown(object sender, MouseEventArgs e)
        {
            base.OnMouseDown(sender, e);

            if (e.WidgetUnderMouse)
            {
                this.StartTracking(e.Position);
            }
#if !WINDOWS
            if (inputTracker == null)
            {
                Guide.BeginShowKeyboardInput(KeyboardInfo.PlayerIndex, KeyboardInfo.Title, KeyboardInfo.Description, Text, EndKeyboardInput, null, UsePasswordMode);
            }
#endif
        }

        public void Update(GameTime gameTime)
        {
            this.caretBlinkTimer += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (this.caretBlinkTimer > 2 * BlinkDuration)
            {
                this.caretBlinkTimer -= 2 * BlinkDuration;
            }

            if (this.inputTracker != null)
            {
                this.inputTracker.Update(gameTime);
            }
        }
       
#if !WINDOWS
        private void EndKeyboardInput(IAsyncResult result)
        {
            string txtBefore = Guide.EndShowKeyboardInput(result);

            if (txtBefore != null)
            {
                // remove all invalid characters to prevent a crash
                string txt = string.Empty;
                foreach (char c in txtBefore)
                {
                    if (CharacterValidCheck(c))
                    {
                        txt += c.ToString();
                    }
                }

                TextChanged(txt);
            }

            Focused = false;
        }
#endif

        private void StartTracking(Vector2 cursorPosition)
        {
            if (this.Text == null)
            {
                this.Text = string.Empty;
            }

            SetState("Edit");

            if (inputTracker == null)
            {
                return;
            }

            this.inputTracker.StartTracking(Text);

            cursorPosition -= ScreenLocation;
            float x = 0;
            int i = 0;
            for (i = 0; i < this.Text.Length; i++)
            {
                float len = Font.MeasureString(Text[i].ToString()).X;
                if (x + (0.75f * len) > cursorPosition.X + this.overlap)
                {
                    break;
                }

                x += len + Font.Spacing;
            }

            this.inputTracker.CaretPosition = i;

            float layerHeight = CurrentState.Layers.First((l) => l.Name == "Caret").Image.Rectangle.Height;
            float y = (0.5f * Size.Y) - (0.5f * layerHeight);

            this.caretLocation = new Vector2(x, y);
        }

        private void UpdateCaretLocation(int caretPosition)
        {
            float x = 0;
            int i = 0;
            for (i = 0; i < caretPosition; i++)
            {
                float len = Font.MeasureString(Text[i].ToString()).X + Font.Spacing;
                x += len;
            }

            x -= 0.5f * Font.Spacing;

            float layerHeight = CurrentState.Layers.First((l) => l.Name == "Caret").Image.Rectangle.Height;
            float y = (0.5f * Size.Y) - (0.5f * layerHeight);

            this.caretLocation = new Vector2(x, y);
            this.caretBlinkTimer = 0;

            // evaluate the overlap on the left which is clamped
            if ((caretPosition == Text.Length)
                || (x < this.overlap + CaretMarginLeft)
                || (x > this.overlap + ScissorRectangle.Width - CaretMarginRight))
            {
                this.overlap = Math.Max(x + CaretMarginRight - ScissorRectangle.Width, 0);
            }
        }

        private void StopTracking()
        {
            SetState("Normal");

            if (inputTracker == null)
            {
                return;
            }

            this.inputTracker.StopTracking();

            if (Parent != null && Parent is ContainerWidget)
            {
                ((ContainerWidget)Parent).UnfocusChild();
            }
        }

        private void TextChanged(string txt)
        {
            if (txt.EndsWith("\n"))
            {
                txt = txt.TrimEnd('\n');
                this.StopTracking();
            }

            string character = Font.Characters.Contains('*') ? "*" : "x";

            if (this.UsePasswordMode)
            {
                string pw = string.Empty;
                for (int i = 0; i < txt.Length; i++)
                {
                    pw += character;
                }

                Text = pw;
            }
            else
            {
                this.Text = txt;
            }

            this.caretBlinkTimer = 0;
        }

        private bool CharacterValidCheck(char character)
        {
            if (character == (char)(int)Keys.Enter || character == (char)(int)Keys.Escape)
            {
                this.StopTracking();
                return false;
            }

            if (this.UsePasswordMode)
            {
                return true;
            }

            return Font.Characters.Contains(character);
        }
    }
}
